// 一.请说出下列最终的执行结果，并解释为什么

// var a = [];
// for(var i = 0; i < 10; i++){
//     a[i] = function () {
//         console.log(i)
//     }
// }
//
// a[6]()

// 答：结果为10，因为var在循环到10的时候结束了，并没有被重置，i在循环体外仍然生效，
// 所以 a[6]会结果为10

// 二.请说出下列最终的执行结果，并解释为什么

// var tmp = 123;
//
// if (true) {
//     console.log(tmp)
//     let tmp
// }
// 答：会报错，因为let声明的变量必须先声明后才能使用，不能变量提升，
// 并且作用域只在 {} 之间。

// 三.结合ES6新语法，用最简单的方式找出数组中的最小值。
var arr = [12, 34, 32, 89, 4]

// 答：
const minFuc = (arr) => Math.min(...arr)
console.log(minFuc(arr))

// 四.请详细说明 var、let、const 三种申明变量的方式之间的具体差别。
// var：
// 定义变量
// 有变量声明提升情况，即可先使用（此时使用时，值为undefined），后声明
// 作用域在全局或某个函数内（全局作用域/函数作用域）
// let：
// 定义变量
// 无变量声明提升情况，必须先声明，后使用
// 作用域是块级作用域
// const：
// 定义常量
// 声明时必须赋值
// 只读特性，声明后不能修改其指向的内存地址

// 五.说出下列最终执行结果，并解释原因。
    var a = 10;
    var obj = {
        a: 20,
        fn() {
            setTimeout(() => {
                console.log(this.a)
            })
        }
    }
    obj.fn()
//
// 答：一般来说，this 指向遵循谁调用就指向谁的原则，即 this 指向谁是在方法被调用时才能确定。
// 如果上面示例 setTimeout 中以 function 方式定义同样的函数，当 obj.fn() 时，输出就是 10。这个 10 就是全局对象（window）中 a 的值。
// 因为调用 setTimeout 中的回调函数的对象其实是 全局对象（window），
// 所以 this 指向 全局对象（window）。
// 而箭头函数中 this 与函数外部的 this 是保持一致的。
// 箭头函数可以看作仅仅是个代码块，其实箭头函数本身是没有 this 的，
// 其使用的this来自作用域链，来自外层函数的作用域，
// 也就是说箭头函数会捕获其所在上下文的 this 作为自己的 this。
// 就示例来看，obj 调用 fn 时，
// setTimeout 的箭头函数回调的 this 就是 fn 中的 this，
// 而这个 this 就是指向的 obj，所以 obj.fn() 输出结果为 20。

// 六.简述 Symbol 类型的用途。
// 答：
// Symbol 是 ES6 中新增的基本数据类型，表示独一无二的值。

// 用途：
// 1.可作为对象的属性名（key 值）
// 2.Symbol 作为对象的属性名，可以模拟对象的私有属性
// 因为 Symbol 独一无二，所以无法通过 obj.属性名 的方式直接获取属性值。且 Symbol 类型的 key 不能通过 Object.keys() 或者 for...in 来枚举，Object.getOwnPropertyNames(obj) 也获取不到。
// 但能通过 Reflect.ownKeys()，该方法能获取对象上所有键的列表，包括字符串和 symbol。
// 防止对象属性名的冲突
// 当不同的库希望向对象添加属性，就有可能出现重名风险，而 Symbol 的独一无二的特性就可避免这个风险。
// 七.谈谈什么是浅拷贝,什么是深拷贝

// 答：
// 一般来讲，当拷贝发生在简单数据类型上时，则类似于直接赋值，会拷贝其本身。而浅、深拷贝多与对象的拷贝有关。
// 浅拷贝：
// 浅拷贝只拷贝了对象的一层，而对象的深层次级别就只拷贝了引用，也就是共用被拷贝对象的堆内存地址。

// 深拷贝:
// 深拷贝则是拷贝了对象的每一次数据，相当于按照目标生成一个全新的对象，创建了自己的新堆内存地址。

// 八.简述 TypeScript 与 JavaScript 之间的关系

// 答：
// TypeScript 是基于 JavaScript 之上的编程语言，通常说 TS 是 JS 的超集（扩展集）。
// TS 在 JS 的基础上拓展了新的特性：更强大的类型系统和对 ES6+ 新特性的支持。TS 最终会被编译成 JS，所以任意一种支持 JS 运行的环境都可以用 TS 来开发。
// 更强大的类型系统： 类似使用 flow 一样，可以在开发的时候对变量、对象、函数等设置类型，使用强类型语言的能力，以避免开发时有可能出现的类型错误，提高开发效率和代码的可靠性。
// ES6+ 新特性的支持： TS 编译时，会转换新特性，编译时最低会编译到 ES3 版本，所以其兼容性非常好


// 九.谈谈你所认为的 TypeScript 的优缺点。

// 答：
// 优点:
// 强类型系统避免开发中的类型错误，且在编译时就能发现错误，提高了代码可靠性。
// 对 ES6+ 新特性的支持，可任意使用新方法、新特性，通过编译，将代码编译成适用版本，提高了兼容性
// 有健全、完善的语言生态
// 相对于 JS 开发者，TS 是渐进式的，即可以完全用 JS 的方式编写 TS，当遇到或学到某个 TS 相关特性时再使用也是可以的。

// 缺点:
// 语言本身多了很多概念，诸如：接口、泛型、枚举等，这样增加了开发者的学习成本。
// 项目初期，TS 会增加一些开发成本，需要编写很多类型声明。
// 一般来说，对于小型、短周期的项目使用 JS 更好，其相比之下更灵活、便捷。
// 但对于大型项目、需要长期维护的，TS 是更好的选择，前期的准备工作所花的成本是可以接受的，也是一劳永逸的

// 十、描述引用计数的工作原理和优缺点。

// 答：

// 原理：
// 在对象内部维护一个引用计数器，用于记录对象被引用的次数
// 当对象的引用关系被改变时，就修改引用的数字。即该对象被另一个变量引用了，建立了引用关系，引用计数器就加一；若断开了一个引用关系，引用计数器就减一。
// 当引用计数器的数字变成 0 时，则被判定为垃圾，当前 GC 会立即工作，对该对象所占空间进行回收。

// 优点：
// 发现垃圾时会立即回收。
// 因为其判断计数器是否为 0，只要为 0，就判断为垃圾，然后立即回收。
// 最大限度的坚守程序的暂停。
// 因为在实时监控计数器为 0 的对象，当内存空间即将占满时，GC 可以立即处理垃圾对象来释放空间，如此保证当前内存没有被占满的时候

// 缺点：
// 无法回收循环引用的对象
// 时间开销大
// 因为当前的引用计数需要去维护一个数值的变化，所以要时刻监控当前对象的引用数值是否需要修改。当出现多个这种修改时，就会开销更多的时间


// 十一.描述标记整理算法的工作流程。
// 标记整理算法可以看作标记清除算法的增强操作。相比单纯的标记清除，新增了空间整理的步骤，以解决内存空间碎片化问题。
//
// 遍历所有对象，对当前可达的活动对象进行标记
// 进行整理操作。移动当前活动对象，使这些活动对象在内存地址上变成连续的位置
// 遍历所有对象，对没有标记的对象进行垃圾回收，同时抹除之前已标记对象的标记

// 十二、描述 V8 中新生代存储区垃圾回收的流程。
// V8 是基于分代回收的策略，所以在内存区分为了新生代存储区（小空间）和老生代存储区（大空间）。
// 新生代存储区:
//     大小为 64 位 32 M / 32 位 16 M。用于存储新生代对象 —— 存活时间较短的对象（如：局部作用域中的变量）

// 老生代存储区:
//     大小为 64 位 1.4 G / 32 位 700 M。用于存储老生代对象 —— 存活时间较长的对象（如：全局作用域中的变量、闭包中存放的数据）

// 新生代存储区回收的流程：
// 回收过程采用 复制算法 和 标记整理算法
// 将新生代内存区分为两个等大的空间，一个为使用空间(From)，一个为空闲空间（To）
// 将活动对象存储于 From 空间（使用空间）中（To 空间此时空闲，未使用）
// 当 From 空间（使用空间）使用到一定程度触发 GC 时，先在 From 中进行标记整理，然后将整理后的活动对象拷贝至 To 空间中
// 完全释放 From 空间。此时，原 From 变成了空闲空间（To），原 To 变成了使用空间（From）。
// 细节情况：
// 在拷贝至 To 空间的过程中，可能出现晋升情况，即将新生代对象移动至老生代存储区保存。
//
// 晋升判断标准：
//
// 一轮 GC 后还存活的新生代对象
// To 空间的使用率超过 25%

// 十三.描述增量标记算法在何时使用及工作原理。
// 何时使用：
// 增量标记算法是在 V8 引擎的老生代对象回收中使用的 GC 算法，用来对回收效率进行优化。
//
// 工作原理：
// 因为 程序执行 和 GC 操作 是非并行的，即同时只能有一个在工作。
//
// 增量标记算法的原理就在于将 GC 的遍历对象进行标记的操作，分成多个段进行执行。
//
// 但进行遍历编辑时，先遍历标记一部分，然后停下，让程序执行；程序执行一段时间后，再进行一部分的遍历标记。如此，使遍历标记与程序的执行交替进行。当所有标记完成，再进行清除回收。



